package com.ml.loan.sys.controller;

import cn.hutool.crypto.SecureUtil;
import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
import com.google.code.kaptcha.Producer;
import com.ml.loan.common.BaseController;
import com.ml.loan.common.annotaion.AccessLimit;
import com.ml.loan.common.annotaion.OperLog;
import com.ml.loan.common.model.JsonResult;
import com.ml.loan.common.redis.RedisService;
import com.ml.loan.common.redis.keys.UserKey;
import com.ml.loan.common.shiro.LoginType;
import com.ml.loan.common.shiro.UserToken;
import com.ml.loan.common.utils.UserAgentGetter;
import com.ml.loan.sys.model.Authorities;
import com.ml.loan.sys.model.LoginRecord;
import com.ml.loan.sys.model.User;
import com.ml.loan.sys.service.AuthoritiesService;
import com.ml.loan.sys.service.LoginRecordService;
import com.ml.loan.sys.service.SysWebInfoService;
import com.ml.loan.sys.service.UserService;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * MainController
 */
@Controller
public class MainController extends BaseController implements ErrorController {
    @Autowired
    private AuthoritiesService authoritiesService;
    @Autowired
    private LoginRecordService loginRecordService;
    @Autowired
    private UserService userService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private SysWebInfoService sysWebInfoService;
    @Autowired
    private Producer kaptchaProducer;

    @Autowired
    private CaptchaService captchaService;

    // 主页
    @RequestMapping({"/", "/index"})
    public String index(Model model) {
        if (getLoginUser() == null) {
            return "redirect:login";
        }
        List<Authorities> authorities = authoritiesService.listByUserId(getLoginUserId());
        List<Map<String, Object>> menuTree = getMenuTree(authorities, -1);
        model.addAttribute("menus", menuTree);
        model.addAttribute("loginUser", userService.getByUserId(getLoginUserId()));
        model.addAttribute("webInfo", sysWebInfoService.getWebInfo());
        return "index.html";
    }

    // 登录页
    @GetMapping("/login")
    public String login(Model model) {
        if (getLoginUser() != null) {
            return "redirect:index";
        }
        model.addAttribute("webInfo", sysWebInfoService.getWebInfo());
        return "login.html";
    }

    @GetMapping("/assets/captcha")
    public void checkImgCode(HttpServletRequest request, HttpServletResponse response) {
        //设置相应类型,告诉浏览器输出的内容为图片
        response.setContentType("image/jpeg");

        //设置响应头信息，告诉浏览器不要缓存此内容
        response.setHeader("pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expire", 0);
        try {
            String randomNum = kaptchaProducer.createText();
            BufferedImage image = kaptchaProducer.createImage(randomNum);
            request.getSession().setAttribute("verifyCodeValue", randomNum);
            OutputStream out = response.getOutputStream();
            ImageIO.write(image, "JPEG", out);
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 登录
    @ResponseBody
    @PostMapping("/login")
    @OperLog(module = "LOGIN", desc = "用户登录", result = true)
    @AccessLimit(seconds = 60, maxCount = 5, message = "{0}秒以后再操作")
    public JsonResult doLogin(String username, String password, String code, String captcha, HttpServletRequest request) {
//        String verifyCodeValue = String.valueOf(request.getSession().getAttribute("verifyCodeValue"));
        if (StringUtils.isAnyBlank(username, password)) {
            return JsonResult.error("账号密码不能为空");
        }
        CaptchaVO captchaVO = new CaptchaVO();
        captchaVO.setCaptchaVerification(captcha);
        final ResponseModel verification = captchaService.verification(captchaVO);
        if (verification.isSuccess() == false) {
            return JsonResult.error(verification.getRepMsg());
        }
//        if (StringUtils.isAnyBlank(code, verifyCodeValue) || !code.equalsIgnoreCase(verifyCodeValue)) {
//            return JsonResult.error("验证码错误");
//        }
        User user = userService.getByUsername(username);
        if (user != null && user.getType().intValue() == 1) {
            return JsonResult.error("请输入正确的账号");
        }
        String key = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + "-" + username;
        Integer errnum = redisService.get(UserKey.mngUserLock, key, Integer.class);
        if (errnum != null && errnum >= 5) {
            return JsonResult.error("今日密码错误次数超限，请明日再试");
        }
        try {
            UserToken userToken = new UserToken(username, password, LoginType.USER.toString());
            userToken.setRememberMe(false);
            SecurityUtils.getSubject().login(userToken);

            addLoginRecord(getLoginUserId(), request);
            //清空错误次数
            redisService.delete(UserKey.mngUserLock, key);
            return JsonResult.ok("登录成功");
        } catch (IncorrectCredentialsException ice) {
            Long num = redisService.increment(UserKey.mngUserLock, key, 1L);
            return JsonResult.error(MessageFormat.format("密码错误，超过5次将被限制登录，剩余{0}次", 5 - num));
        } catch (UnknownAccountException uae) {
            return JsonResult.error("账号不存在");
        } catch (LockedAccountException e) {
            return JsonResult.error("账号被锁定");
        } catch (ExcessiveAttemptsException eae) {
            return JsonResult.error("操作频繁，请稍后再试");
        }
    }


    // 控制台
    @RequestMapping("/console")
    public String console(Model model) {
        /*if (ShiroExt.hasPermission("index:data")){
            return "system/console.html";
        } else {
            model.addAttribute("nikeName",getLoginNickName());
            model.addAttribute("webInfo",sysWebInfoService.getWebInfo());
            return "system/welcome.html";
        }*/
        return "system/console.html";
    }

    // 消息弹窗页
    @RequestMapping("/tpl/message")
    public String message() {
        return "system/tpl-message.html";
    }

    // 修改密码弹窗页
    @RequestMapping("/tpl/password")
    public String password() {
        return "system/tpl-password.html";
    }

    // 主题设置弹窗页
    @RequestMapping("/tpl/theme")
    public String theme() {
        return "system/tpl-theme.html";
    }

    // 便签设置弹窗页
    @RequestMapping("/tpl/note")
    public String note() {
        return "system/tpl-note.html";
    }

    // 授权信息弹窗页
    @RequestMapping("/tpl/about")
    public String auth(Model model) {
        model.addAttribute("webInfo", sysWebInfoService.getWebInfo());
        return "system/tpl-about.html";
    }

    // 错误页
    @RequestMapping("/error")
    public String error(String code, Model model) {
        model.addAttribute("webInfo", sysWebInfoService.getWebInfo());
        if ("403".equals(code)) {
            return "error/403.html";
        }
        return "error/404.html";
    }

    @RequestMapping("/exception/404")
    public String error_404(Model model) {
        model.addAttribute("webInfo", sysWebInfoService.getWebInfo());
        return "error/404.html";
    }

    @RequestMapping("/exception/500")
    public String error_500(Model model) {
        model.addAttribute("webInfo", sysWebInfoService.getWebInfo());
        return "error/500.html";
    }


    public String getErrorPath() {
        return "/error";
    }

    // 递归转化树形菜单
    private List<Map<String, Object>> getMenuTree(List<Authorities> authorities, Integer parentId) {
        List<Map<String, Object>> list = new ArrayList<>();
        for (int i = 0; i < authorities.size(); i++) {
            Authorities temp = authorities.get(i);
            if (temp.getIsMenu() == 0 && parentId.intValue() == temp.getParentId()) {
                Map<String, Object> map = new HashMap<>();
                map.put("menuId", temp.getAuthorityId());
                map.put("menuName", temp.getAuthorityName());
                map.put("menuIcon", temp.getMenuIcon());
                map.put("menuUrl", StringUtils.isBlank(temp.getMenuUrl()) ? "javascript:;" : (temp.getMenuUrl()));
                map.put("subMenus", getMenuTree(authorities, authorities.get(i).getAuthorityId()));
                list.add(map);
            }
        }
        return list;
    }

    // 添加登录日志
    private void addLoginRecord(Integer userId, HttpServletRequest request) {
        UserAgentGetter agentGetter = new UserAgentGetter(request);
        LoginRecord loginRecord = new LoginRecord();
        loginRecord.setUserId(userId);
        loginRecord.setOsName(agentGetter.getOS());
        loginRecord.setDevice(agentGetter.getDevice());
        loginRecord.setBrowserType(agentGetter.getBrowser());
        loginRecord.setIpAddress(agentGetter.getIP());
        loginRecordService.save(loginRecord);

        User user = new User();
        user.setUserId(userId);
        user.setLastLoginTime(LocalDateTime.now());
        userService.updateById(user);
        if (userId != null) {
            //清除锁屏
            redisService.delete(UserKey.userLockPage, userId.toString());
        }
    }

    /**
     * @param model
     * @return java.lang.String
     * @Description 跳转锁屏页
     * @Author mickey
     * @Date 2019/5/26 18:36
     **/
    @RequestMapping("/lockPage")
    public String lockPage(Model model) {
        model.addAttribute("loginUser", userService.getByUserId(getLoginUserId()));
        model.addAttribute("webInfo", sysWebInfoService.getWebInfo());
        String psw = redisService.get(UserKey.userLockPage, getLoginUserId().toString(), String.class);
        if (StringUtils.isNotBlank(psw)) {
            model.addAttribute("lockTime", redisService.getExpire(UserKey.userLockPage, getLoginUserId().toString()));
            return "layout/lockPage.html";
        } else {
            return "redirect:index";
        }
    }

    /**
     * @param psw
     * @param request
     * @return com.weilian.huabei.common.JsonResult
     * @Description 设置锁屏密码
     * @Author mickey
     * @Date 2019/5/26 18:37
     **/
    @ResponseBody
    @PostMapping("/setLockPsw")
    public JsonResult setLockPsw(String psw, HttpServletRequest request) {
        if (redisService.set(UserKey.userLockPage, getLoginUserId().toString(), psw)) {
            return JsonResult.ok();
        } else {
            return JsonResult.error();
        }
    }

    /**
     * @param request
     * @return com.weilian.huabei.common.JsonResult
     * @Description 验证锁屏状态
     * @Author mickey
     * @Date 2019/5/26 18:37
     **/
    @RequestMapping("/checkLockPsw")
    @ResponseBody
    public JsonResult checkLockPsw(HttpServletRequest request) {
        String psw = redisService.get(UserKey.userLockPage, getLoginUserId().toString(), String.class);
        JsonResult result = JsonResult.ok();
        result.put("state", StringUtils.isNotBlank(psw));
        return result;
    }

    /**
     * @param psw
     * @param request
     * @return com.weilian.huabei.common.JsonResult
     * @Description 屏幕解锁
     * @Author mickey
     * @Date 2019/5/26 18:37
     **/
    @ResponseBody
    @PostMapping("/unLockPsw")
    public JsonResult unLockPsw(String psw, HttpServletRequest request) {
        if (StringUtils.isNotBlank(psw)) {
            String lockpsw = redisService.get(UserKey.userLockPage, getLoginUserId().toString(), String.class);
            if (psw.equals(lockpsw)) {
                if (redisService.delete(UserKey.userLockPage, getLoginUserId().toString())) {
                    return JsonResult.ok();
                }
                return JsonResult.error("解锁失败");
            } else {
                return JsonResult.error("密码不正确");
            }
        } else {
            return JsonResult.error("请输入锁屏密码");
        }
    }


    @RequestMapping("/tpl/lockScreen")
    public String lockScreen(String psw, Model model) {
        model.addAttribute("psw", SecureUtil.md5(psw));
        model.addAttribute("webInfo", sysWebInfoService.getWebInfo());
        return "layout/lock-screen.html";
    }


    @RequestMapping("/adminTool")
    public String adminTool(Model model) {
        return "system/adminTool.html";
    }

    @RequestMapping("/adminTool/searchStringRedisKey")
    @ResponseBody
    @OperLog(module = "SYS", desc = "系统操作-查询redisValue", result = true)
    public JsonResult searchRedisKey(String key) {
        if (!"admin".equals(getLoginUserName())) {
            return JsonResult.error("只能admin账号操作");
        }
        key = StringUtils.remove(key, " ");
        if (StringUtils.isBlank(key)) {
            return JsonResult.ok().put("data", "");
        }
        if (StringUtils.isNotBlank(key) && key.contains("*")) {
            return JsonResult.error("不允许携带通配符");
        }
        String value;
        try {
            value = redisService.getStringValue(key);
        } catch (Exception e) {
            return JsonResult.error("获取数据异常，请检查key是否有误");
        }
        return JsonResult.ok().put("data", value);
    }


    @RequestMapping("/adminTool/delStringRedisKey")
    @ResponseBody
    @OperLog(module = "SYS", desc = "系统操作-清除redisValue", result = true)
    public JsonResult delRedisKey(String key) {
        if (!"admin".equals(getLoginUserName())) {
            return JsonResult.error("只能admin账号操作");
        }
        key = StringUtils.remove(key, " ");
        if (StringUtils.isBlank(key)) {
            return JsonResult.ok().put("data", "");
        }
        if (StringUtils.isNotBlank(key) && key.contains("*")) {
            return JsonResult.error("不允许携带通配符");
        }
        String stringValue;
        try {
            stringValue = redisService.getStringValue(key);
        } catch (Exception e) {
            return JsonResult.error("获取数据异常，请检查key是否有误");
        }
        if (StringUtils.isBlank(stringValue)) {
            return JsonResult.error("要清除的数据不存在");
        }
        return JsonResult.ok("删除成功，值：" + stringValue).put("data", redisService.deleteByKey(key));
    }

}
